package asteroids;

public abstract class Vector {

	/**
	 * Returns a position based on the given start position, time and speed.
	 * 
	 * @param deltaT
	 *            The amount of time over which the position must be moved.
	 * @return The position determined based on the given positon, time and
	 *         speed. |result.getX == (pos).getX + deltaT*vel.getX) &&
	 *         result.getY() == pos.getY() + deltaT*vel.getY)
	 * @throws IllegalArgumentException
	 *             The given amount of time is either not a number or is smaller
	 *             than zero. |(deltaT < 0) || (Double.isNaN(deltaT))
	 */
	public static Position move(double deltaT, Position pos, Velocity vel)
			throws IllegalArgumentException {
		if ((deltaT < 0) || (Double.isNaN(deltaT))) {
			throw new IllegalArgumentException();
		}

		else {
			double newX = pos.getX() + deltaT * vel.getX();
			double newY = pos.getY() + deltaT * vel.getY();
			Position newPos = new Position(newX, newY);
			return newPos;
		}
	}

	/**
	 * Calculates the distance between the two given FlyingObjects based on their
	 * position and size.
	 * 
	 * @param thing1
	 *            The first FlyingObjects
	 * @param thing2
	 *            The second FlyingObjects
	 * @return Zero if the same FlyingObjects is given twice. | thing1 == thing2
	 * @return The distance between the given FlyingObjects |result ==
	 *        | Math.sqrt(Math.pow(thing1.getPosition().getX() -
	 *         |thing2.getPosition().getX(),2) +
	 *         |Math.pow(thing1.getPosition().getY() -
	 *         |thing2.getPosition().getY(),2)) - thing1.getSize() +
	 *         |thing2.getSize()
	 * @throws NullPointerException
	 *             One or both of the given FlyingObjects are invalid. |(thing1 == null)
	 *             || (thing2 == null)
	 */
	public static double getDistanceBetween(FlyingObject thing1, FlyingObject thing2)
			throws NullPointerException {
		if ((thing1 == null) || (thing2 == null)) {
			throw new NullPointerException();
		} else if (thing1 == thing2) {
			return 0;
		} else {
			double Xdiff = thing1.getPosition().getX()
					- thing2.getPosition().getX();
			double Ydiff = thing1.getPosition().getY()
					- thing2.getPosition().getY();
			double centerDiff = Xdiff * Xdiff + Ydiff * Ydiff;
			centerDiff = Math.sqrt(centerDiff);
			double sumOfSizes = thing1.getSize() + thing2.getSize();
			double result = centerDiff - sumOfSizes;
			return result;
		}
	}

	/**
	 * 
	 * @param thing1
	 * @param thing2
	 * @return |if(for each deltaT, thing1.move(deltaT), thing2.move(deltaT)->
	 *         thing1.overlap(thing2)==false) then result == POSITIVE_INFINITY
	 * @return |result == T && ( if (thing1.move(T),(thingt2.move(T)) then
	 *         |thing1.overlap(thing2) == true) && (for each t,
	 *         |(thing1.move(T),(thingt2.move(T)), thing1.overlap(thing2) ==
	 *         |true, T =< t )
	 * @throws NullPointerException
	 *             |(thing1==null ||thing2==null)
	 */
	public static double getTimeToCollision(FlyingObject thing1,FlyingObject thing2)
			throws NullPointerException {
		if ((thing1 == null) || (thing2 == null)) {
			throw new NullPointerException();
		}
		double dvx = thing1.getVelocity().getX() - thing2.getVelocity().getX();
		double dvy = thing1.getVelocity().getY() - thing2.getVelocity().getY();

		double drx = thing1.getPosition().getX() - thing2.getPosition().getX();
		double dry = thing1.getPosition().getY() - thing2.getPosition().getY();

		double sigma = (thing1.getSize() + thing2.getSize());

		double vTimesR = (dvx * drx) + (dvy * dry);
		if (vTimesR >= 0) {
			return Double.POSITIVE_INFINITY;
		} else {
			double vTimesV = (dvx * dvx) + (dvy * dvy);
			double rTimesR = (drx * drx) + (dry * dry);
			double D = (vTimesR * vTimesR) - vTimesV
					* (rTimesR - (sigma * sigma));
			if (D <= 0) {
				return Double.POSITIVE_INFINITY;
			} else {
				double sqrtD = Math.sqrt(D);
				double result = (-1) * (vTimesR + sqrtD) / vTimesV;
				if (result >= 0){
				return result;}
				else {return Double.POSITIVE_INFINITY;}
			}
		}
	}
	
	
	/**
	 * Calculates the position where the ships will collide.
	 * 
	 * @param ship1
	 *            The first ship.
	 * @param ship2
	 *            The first ship.
	 * @return Positive infinity if there is no collision |result ==
	 *        | POSITIVE_INFINITY && getTimeToCollision(ship1, ship2) ==
	 *         |POSITIVE_INFINITY
	 * @return The position where the two given ships will collide. |result ==
	 *         |ship1.move(getTimeToCollision(ship1, ship2)).getPosition() +
	 * @throws NullPointerException
	 *             One or both of the given ships is invalid. |ship1 == null ||
	 *             |ship2 == null
	 */
	public static Position getCollisionPosition(FlyingObject thing1, FlyingObject thing2)
			throws NullPointerException {

		if ((thing1 == null) || (thing2 == null)) {
			throw new NullPointerException();
		}

		double T = getTimeToCollision(thing1, thing2);

		if (T == Double.POSITIVE_INFINITY) {
			return new Position(Double.POSITIVE_INFINITY,Double.POSITIVE_INFINITY);
		}

		else {
			double centerX1 = thing1.getPosition().getX() + T
					* thing1.getVelocity().getX();
			double centerY1 = thing1.getPosition().getY() + T
					* thing1.getVelocity().getY();
			double centerX2 = thing2.getPosition().getX() + T
					* thing2.getVelocity().getX();
			double centerY2 = thing2.getPosition().getY() + T
					* thing2.getVelocity().getY();

			double relXPos = (centerX2 - centerX1)
					* (thing1.getSize() / (thing1.getSize() + thing2.getSize()));
			double relYPos = (centerY2 - centerY1)
					* (thing1.getSize() / (thing1.getSize() + thing2.getSize()));

			double xPos = centerX1 + relXPos;
			double yPos = centerY1 + relYPos;
			return new Position(xPos, yPos);

		}
	}
	
}
